#include "ai_reflex.h"



void reflex_algorithm::get_buy_commands( const game_state* gs )
{
	//clears current commands queue (not necessary)
	commands.clear();
	cout << "AI BEGIN" << endl;
	time_t start_time = time( 0 );
	
	commands.push_back( "END" );
	
	unsigned int elapsed_sec = ( unsigned int)( abs( ceil ( difftime( time( 0 ), start_time ) ) ) );

	cout << "Elapsed time: " << ai_algorithms::sec_to_string( elapsed_sec ) << " (will not be added to graph)" << endl;
	cout << "AI END" << endl;
};



void reflex_algorithm::get_commands( const game_state* gs )
{
	//clears current commands queue (not necessary)
	commands.clear();
	cout << "AI BEGIN" << endl;
	time_t start_time = time( 0 );

	returned_units.clear();
	for ( unsigned int i = 0; i < gs->plrs[ gs->current_player_id ]->units.size(); ++i )
		returned_units.push_back( false );

	original_state = gs;
	state = original_state->clone();
	
	generate_commands();
	
	commands.push_back( "NXT" );
	
	state = 0;

	unsigned int elapsed_sec = ( unsigned int)( abs( ceil ( difftime( time( 0 ), start_time ) ) ) );
	time_cons.push_back( elapsed_sec );
	cout << "Elapsed time: " << ai_algorithms::sec_to_string( elapsed_sec ) << endl;
	cout << "AI END" << endl;
};



void reflex_algorithm::generate_commands()
{
	for ( ; ; )
	{
		rem_time = state->plrs[ state->current_player_id ]->get_time();
		//for estimating, how much time it will take to get all units to safe places
		int delta_t = rem_time;

		check_dangerous_places( false );
		//now rem_time == remaining time

		state->plrs[ state->current_player_id ]->set_time( rem_time );
		delta_t = delta_t - rem_time;
		//delta_t == time needed to get all units to safe places

		rem_time = 0;


		check_attackable_places();
		//now rem_time = cost of commands to go back

		delta_t += rem_time;
		//delta_time = cost of commands to go back + defensive commands computet at the beginning

		for ( list< string >::iterator it = fire_commands.begin(); it != fire_commands.end(); ++it )
			commands.push_back( *it );
		
		move_commands.clear();

		//increases time by delta_t because these commands were only simulated, but not done
		state->plrs[ state->current_player_id ]->set_time( state->plrs[ state->current_player_id ]->get_time() + delta_t );

		check_dangerous_places( true );

		for ( list< string >::iterator it = move_commands.begin(); it != move_commands.end(); ++it )
			commands.push_back( *it );

		bool stop_iteration = ( move_commands.empty() && fire_commands.empty() );
		move_commands.clear();
		fire_commands.clear();

		delete state;

		//if some command found -> do one more iteration
		if ( stop_iteration )
			break;
		
		state = original_state->clone();

		for ( list< string >::iterator it = commands.begin(); it != commands.end(); ++it )
			ai_support_computations::apply_command( state, *it );

		//state->update_structures();

	}
	
};



pair< int, int > reflex_algorithm::find_safe_place( unit* u, game_state* state )
{
	if ( state->dangerous_places_nobody == 0 )
		state->dangerous_places_nobody = ai_support_computations::map_places_dangerous_nobody( state );
	unsigned int d = 30000;
	int tm;
	unsigned int dang = state->dangerous_places_nobody->get( u->get_x(), u->get_y() );
	pair< int, int > s_place = make_pair< int, int >( -1, -1 );
	//compute search ranges (square where to search for possibilities)
	unsigned int search_top = ( ( ( int )u->get_y() - ( int )( u->get_player()->get_time() / u->get_cost_move() ) ) > 0 ) ? ( ( u->get_y() - ( u->get_player()->get_time() / u->get_cost_move() ) ) ) : 0;
	unsigned int search_left = ( ( ( int )u->get_x() - ( int )( u->get_player()->get_time() / u->get_cost_move() ) ) > 0 ) ? ( ( u->get_x() - ( u->get_player()->get_time() / u->get_cost_move() ) ) ) : 0;
	unsigned int search_bottom = ( ( u->get_y() + ( u->get_player()->get_time() / u->get_cost_move() ) ) < mapa::get_instance()->get_size_y() ) ? ( u->get_y() + ( u->get_player()->get_time() / u->get_cost_move() ) ) : ( mapa::get_instance()->get_size_y() - 1 );
	unsigned int search_right = ( ( u->get_x() + ( u->get_player()->get_time() / u->get_cost_move() ) ) < mapa::get_instance()->get_size_x() ) ? ( u->get_x() + ( u->get_player()->get_time() / u->get_cost_move() ) ) : ( mapa::get_instance()->get_size_x() - 1 );
	for ( unsigned int i = search_left; i <= search_right; ++i )
		for ( unsigned int j = search_top; j <= search_bottom; ++j )
		{
			if ( ( state->map->get( i, j ) < 2000 ) && ( ( dynamic_cast< unit* >( state->objects->get( i, j ) ) ) == 0 ) )
			{
				//same danger, but closer
				if ( state->dangerous_places_nobody->get( i, j ) == dang )
				{
					tm = ai_support_computations::ai_test_move( u->get_x(), u->get_y(), i, j, state );
					if ( ( tm != -1 ) && ( tm < ( int )d ) && ( u->get_player()->get_time() >= tm * ( int )u->get_cost_move() ) )
					{
						s_place = make_pair< unsigned int, unsigned int >( i, j );
						d = ( unsigned int )abs( tm );
					}
				}
				//less dangerous
				else if ( state->dangerous_places_nobody->get( i, j ) < dang )
				{			
					tm = ai_support_computations::ai_test_move( u->get_x(), u->get_y(), i, j, state );
					if ( ( tm != -1 ) && ( u->get_player()->get_time() >= tm * ( int )u->get_cost_move() ) )
					{
						s_place = make_pair< unsigned int, unsigned int >( i, j );
						d = ( unsigned int )abs( tm );
						dang = state->dangerous_places_nobody->get( i, j );
					}
				}
			}
		}

	return s_place;
};



void reflex_algorithm::check_dangerous_places( bool apply )
{
	game_state* test_state;
	if ( apply )
		test_state = state;
	else
		test_state = state->clone();
	for ( unsigned int i = 0; i < test_state->plrs[ test_state->current_player_id ]->units.size(); ++i )
	{
		if ( test_state->dangerous_places_nobody == 0 )
			test_state->dangerous_places_nobody = ai_support_computations::map_places_dangerous_nobody( test_state );
		unit* u = test_state->plrs[ test_state->current_player_id ]->units[ i ];
		//unit is in danger
		if ( test_state->dangerous_places_nobody->get( u->get_x(), u->get_y() ) > 0 )
		{
			cout << "Unit at [" << u->get_x() << "," << u->get_y() << "] is in danger - ";
			pair< int, int > place = find_safe_place( u, test_state );
			//safe place found
			if ( place.first != -1 )
			{
				string cmd = game::create_command( "MOV", u->get_id(), place.first, place.second );
				if ( ai_support_computations::apply_command( test_state, cmd ) )
				{
					if ( apply )
						returned_units[ u->get_id() ] = true;
					move_commands.push_back( cmd );
					test_state->clear_structures();
					cout << "new place: [" << place.first << "," << place.second << "]" << endl;
				}
				else
				{
					cout << "no new place found" << endl;
				}
			}
			else
			{
				cout << "no new place found" << endl;
			}
		}
	}
	rem_time = test_state->plrs[ test_state->current_player_id ]->get_time();
	if ( !apply )
		delete test_state;
	//else
	//	test_state->update_structures();
};



pair< int, int > reflex_algorithm::find_best_target_unit( unit* u, game_state* state )
{
	pair< int, int > targ = make_pair< int, int >( -1, -1 );
	if ( ( int )u->get_cost_fire() > u->get_player()->get_time() )
		return targ;
	int hlth = 30000;
	for ( unsigned int i = 0; i < state->players_count; ++i )
	{
		if ( i == state->current_player_id )
			continue;
		//finds the opponent's unit with lowest health
		for ( unsigned int j = 0; j < state->plrs[ i ]->units.size(); ++j )
		{
			unit* tu = state->plrs[ i ]->units[ j ];
			if ( tu->get_health() < hlth )
			{
				int d = ai_support_computations::ai_test_fire( u->get_x(), u->get_y(), tu->get_x(), tu->get_y(), state );			
				if ( ( d != -1 ) && ( d <= ( int )u->get_range() ) )
				{
					hlth = tu->get_health();
					targ.first = ( int )tu->get_x();
					targ.second = ( int )tu->get_y();
				}
			}
		}
	}
	return targ;
};



pair< int, pair< int, int > > reflex_algorithm::find_best_target( game_state* state )
{
	pair< int, pair< int, int > > comm = make_pair< int, pair< int, int > >( -1, make_pair< int, int >( -1, -1 ) );
	int hlth = 30000;
	//finds unit and target with the lowest health
	for ( unsigned int i = 0; i < state->plrs[ state->current_player_id ]->units.size(); ++i )
	{
		unit* u = state->plrs[ state->current_player_id ]->units[ i ];
		pair< int, int > temp_targ;
		temp_targ = find_best_target_unit( u, state );
		if ( ( temp_targ.first != -1 ) && ( ( dynamic_cast< unit* >( state->objects->get( temp_targ.first, temp_targ.second ) ) )->get_health() < hlth ) )
		{
			hlth = ( dynamic_cast< unit* >( state->objects->get( temp_targ.first, temp_targ.second ) ) )->get_health();
			comm.first = ( int )u->get_id();
			comm.second = temp_targ;
		}
	}
	return comm;
};



bool reflex_algorithm::can_anybody_attack( game_state* state )
{
	if ( state->attackable_places == 0 )
		state->attackable_places = ai_support_computations::map_places_attackable( state );
	for ( unsigned int i = 0; i < state->players_count; ++i )
	{
		if ( i == state->current_player_id )
			continue;
		for ( unsigned int j = 0; j < state->plrs[ i ]->units.size(); ++j )
		{
			unit* u = state->plrs[ i ]->units[ j ];
			if ( state->attackable_places->get( u->get_x(), u->get_y() ) > 0 )
				return true;
		}
	}
	return false;
};



pair < int, pair< int, int > > reflex_algorithm::find_better_place_unit( unit* u, game_state* state )
{
	pair < int, pair< int, int > > place = make_pair< int, pair< int, int > >( -1, make_pair< int, int >( -1, -1 ) );
	
	if ( state->range_places == 0 )
		state->range_places = ai_support_computations::map_places_range( state );

	//coordinates where to go
	int mx = -1;
	int my = -1;

	//temp distance
	int td = ( int )mapa::get_instance()->get_size_x() * ( int )mapa::get_instance()->get_size_y() * 10 * u->get_cost_move();

	//dist = lowest dist to an place where to attack from
	int dist = td;

	//compute search ranges (square where to search for possibilities)
	for ( unsigned int tx = 0; tx < mapa::get_instance()->get_size_x(); ++tx )
		for ( unsigned int ty = 0; ty < mapa::get_instance()->get_size_y(); ++ty )
			if ( ( *state->range_places )[ u->get_id() ]->get( tx, ty ) > 0 )
			{
				td = ai_support_computations::ai_test_move( u->get_x(), u->get_y(), tx, ty, state );
				if ( td == -1 )
					continue;
				//closer place
				if ( td < dist )
				{
					dist = td;
					mx = tx;
					my = ty;
				}
				//same distance, more targets
				else if ( ( td == dist ) && ( mx != -1 ) && ( my != -1 ) )
					if ( ( *state->range_places )[ u->get_id() ]->get( tx, ty ) > ( ( *state->range_places )[ u->get_id() ]->get( mx, my ) ) )
					{
						mx = tx;
						my = ty;
					}
			}

	//no place found
	if ( ( mx == -1 ) || ( my == -1 ) || ( dist == 0 ) )
		return place;
	mapa* mp = mapa::get_instance();

	//move left
	if ( ( u->get_x() > 0 ) && ( dynamic_cast< unit* >( state->objects->get( u->get_x() - 1, u->get_y() ) ) == 0 ) )
	{
		td = ai_support_computations::ai_test_move( u->get_x() - 1, u->get_y(), mx, my, state );
		if ( ( td != -1 ) && ( dist > td ) )
		{
			dist = td;
			place.first = td;
			place.second.first = u->get_x() - 1;
			place.second.second = u->get_y();
		}		
	}
	//move right
	if ( ( u->get_x() < mp->get_size_x() - 1 ) && ( dynamic_cast< unit* >( state->objects->get( u->get_x() + 1, u->get_y() ) ) == 0 ) )
	{
		td = ai_support_computations::ai_test_move( u->get_x() + 1, u->get_y(), mx, my, state );
		if ( ( td != -1 ) && ( dist > td ) )
		{
			dist = td;
			place.first = td;
			place.second.first = u->get_x() + 1;
			place.second.second = u->get_y();
		}	
	}
	//move up
	if ( ( u->get_y() > 0 ) && ( dynamic_cast< unit* >( state->objects->get( u->get_x(), u->get_y() - 1 ) ) == 0 ) )
	{
		td = ai_support_computations::ai_test_move( u->get_x(), u->get_y() - 1, mx, my, state );
		if ( ( td != -1 ) && ( dist > td ) )
		{
			dist = td;
			place.first = td;
			place.second.first = u->get_x();
			place.second.second = u->get_y() - 1;
		}	
	}
	//move down
	if ( ( u->get_y() < mp->get_size_y() - 1 ) && ( dynamic_cast< unit* >( state->objects->get( u->get_x(), u->get_y() + 1 ) ) == 0 ) )
	{
		td = ai_support_computations::ai_test_move( u->get_x(), u->get_y() + 1, mx, my, state );
		if ( ( td != -1 ) && ( dist > td ) )
		{
			dist = td;
			place.first = td;
			place.second.first = u->get_x();
			place.second.second = u->get_y() + 1;
		}	
	}

	//not enough time for move (and move back if dangerou place)
	if ( state->dangerous_places_nobody == 0 )
		state->dangerous_places_nobody = ai_support_computations::map_places_dangerous_nobody( state );
	unsigned int multi_back = ( state->dangerous_places_nobody->get( place.second.first, place.second.second ) > 0 ) ? 2 : 1;
	if ( ( ( int )multi_back * ( int )u->get_cost_move() * ai_support_computations::ai_test_move( u->get_x(), u->get_y(), place.second.first, place.second.second, state ) ) > u->get_player()->get_time() )
		return make_pair< int, pair< int, int > >( -1, make_pair< int, int >( -1, -1 ) );

	place.first = place.first * u->get_cost_move();
	return place;
};



pair< int, pair< int, int > > reflex_algorithm::find_better_place( game_state* state )
{
	pair< int, pair< int, int > > comm = make_pair< int, pair< int, int > >( -1, make_pair< int, int >( -1, -1 ) );
	int dist = ( int )mapa::get_instance()->get_size_x() * ( int )mapa::get_instance()->get_size_y() * 10 * 100;
	//finds unit, that can get closest to the opponent
	for ( unsigned int i = 0; i < state->plrs[ state->current_player_id ]->units.size(); ++i )
	{
		if ( returned_units[ i ] )
			continue;
		unit* u = state->plrs[ state->current_player_id ]->units[ i ];
		pair < int, pair< int, int > > res = find_better_place_unit( u, state );
		if ( res.first != -1 )
		{
			if ( res.first < dist )
			{
				dist = res.first;
				comm.first = u->get_id();
				comm.second = res.second;
			}
		}
	}
	return comm;
};



void reflex_algorithm::find_target_or_move( game_state* state )
{
	bool move = true;
	if ( can_anybody_attack( state ) )
	{
		pair< int, pair< int, int > > comm = find_best_target( state );
		//target found
		if ( comm.first != -1 )
		{
			move = false;
			string cmd = game::create_command( "ATT", comm.first, comm.second.first, comm.second.second );
			if ( ai_support_computations::apply_command( state, cmd ) )
			{
				state->clear_structures();
				fire_commands.push_back( cmd );
				//state->update_structures();
				unit* au = state->plrs[ state->current_player_id ]->units[ comm.first ];
				cout << "Unit at [" << au->get_x() << "," << au->get_y() << "] will attack to [" << comm.second.first << "," << comm.second.second << "]" << endl;
				//recursive call
				find_target_or_move( state );
			}
		}
	}
	if ( move )
	{
		pair< int, pair< int, int > > comm = find_better_place( state );
		//closer place found
		if ( comm.first != -1 )
		{
			//temp_time == actual time
			int temp_time = state->plrs[ state->current_player_id ]->get_time();
			string cmd = game::create_command( "MOV", comm.first, comm.second.first, comm.second.second );
			int prevx = state->plrs[ state->current_player_id ]->units[ comm.first ]->get_x();
			int prevy = state->plrs[ state->current_player_id ]->units[ comm.first ]->get_y();
			if ( ai_support_computations::apply_command( state, cmd ) )
			{
				state->clear_structures();
				if ( state->dangerous_places_nobody == 0 )
					state->dangerous_places_nobody = ai_support_computations::map_places_dangerous_nobody( state );
				if ( state->dangerous_places_nobody->get( comm.second.first, comm.second.second ) > 0 )
				{
					//sets time = time - cost_move, so unit can get back
					state->plrs[ state->current_player_id ]->set_time( state->plrs[ state->current_player_id ]->get_time() - ( temp_time - state->plrs[ state->current_player_id ]->get_time() ) );
					//rem_time += cost of movement back
					rem_time += ( temp_time - state->plrs[ state->current_player_id ]->get_time() );
				}
				fire_commands.push_back( cmd );
				//state->update_structures();
				cout << "Unit at [" << prevx << "," << prevy << "] will move to [" << comm.second.first << "," << comm.second.second << "]" << endl;
				//recursive call
				find_target_or_move( state );
			}
		}
	}
};



void reflex_algorithm::check_attackable_places()
{
	game_state* test_state = state->clone();
	find_target_or_move( test_state );
	delete state;
	state = test_state;
};